#include "image.hpp"
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>

namespace amber {

Image::Image(cv::Mat image, string name)
    : image(image)
    , name(name)
{
}

Image::Image(std::string data, std::string name)
{
    cv::InputArray encodedImage = cv::_InputArray((const uchar*)data.data(), data.size());
    // build cv::Mat and call Image(cv::Mat image, string name);
    new (this) Image(cv::imdecode(encodedImage, CV_LOAD_IMAGE_COLOR), name);
}
#ifdef AMBER_USE_CAFFE_BLOB
Image::Image(caffe::Blob<float>* blob, int width, int height, std::string name)
{

    float* imageFloatData = blob->mutable_cpu_data();

    cv::Mat image = cv::Mat::zeros(height, width, CV_8UC3);
    uchar* imageBytes = image.data;

    int channels = image.channels();

    // 转换注意三点：
    // 1.将整数转为浮点
    // 2.除255，标准化（所有像素在0-1之间）
    // 3.将HWC主序转换为NCHW
    for (int h = 0; h < height; ++h) {
        uchar* ptr = imageBytes + h * width * channels;
        int pixelIndex = 0;
        for (int w = 0; w < width; ++w) {
            for (int c = 0; c < channels; ++c) {
                int datumIndex = (c * height + h) * width + w;
                ptr[pixelIndex] = static_cast<uchar>(imageFloatData[datumIndex] * 255.0f);
                ++pixelIndex;
            }
        }
    }
    this->image = image;
}
#endif // AMBER_USE_CAFFE_BLOB

Image::Image(std::vector<Image> list, int width, int height, std::string name)
{
    cv::Mat background(height, width, CV_8UC3, cv::Scalar(255, 255, 255));

    int row = 0;
    int col = 0;
    int height_max = 0;
    for (auto image : list) {
        // 若宽度不足，则换行
        if (col + image.image.cols > background.cols) {
            // 换行
            col = 0;
            row += height_max;
            // 换行后重置改行图片的最高高度
            height_max = 0;
        }
        // 检查剩余空间是否满足填充
        if (background.rows - row < image.image.rows || background.cols - col < image.image.cols) {
            break;
        }
        // 画出感兴趣的区域（ROI）
        auto roi = background(cv::Rect(col, row, image.image.cols, image.image.rows));
        // 遮蔽层，copyTo 规定像素为 1 ，才会覆盖，所以用的是 CV_8UC1 灰度图
        cv::Mat mask = cv::Mat(image.image.rows, image.image.cols, CV_8UC1, cv::Scalar(1));
        image.image.copyTo(roi, mask);
        // 前移
        col += image.image.cols;
        // 设置最大图片高度
        height_max = std::max(height_max, image.image.rows);
    }

    new (this) Image(background, name);
}
cv::Mat Image::Crop(int row, int col, int width, int height)
{
    if (row > this->image.rows || col > this->image.cols) {
        return cv::Mat(0, 0, CV_8UC3);
    }
    return cv::Mat(this->image, cv::Range(row, std::min(row + height, this->image.rows)), cv::Range(col, std::min(col + width, this->image.cols)));
}
/**
 * 分割图片
 */
std::vector<Image> Image::Split(int width, int height, bool filling)
{
    std::vector<Image> list;
    for (int row = 0; row < this->image.rows; row += height) {
        int height_crop = height;
        if (filling == false && row + height > this->image.rows) {
            height_crop = this->image.rows - row;
        }
        for (int col = 0; col < this->image.cols; col += width) {
            int width_crop = width;
            if (filling == false && col + width > this->image.cols) {
                width_crop = this->image.cols - col;
            }
            auto image_crop = this->Crop(row, col, width_crop, height_crop);
            if (filling == false) {
                list.push_back(Image(image_crop));
            } else {
                auto background = cv::Mat(height_crop, width_crop, CV_8UC3, cv::Scalar(255, 255, 255));
                // 画出感兴趣的区域（ROI）
                auto roi = background(cv::Rect(0, 0, image_crop.cols, image_crop.rows));
                // 遮蔽层，copyTo 规定像素为 1 ，才会覆盖，所以用的是 CV_8UC1 灰度图
                cv::Mat mask = cv::Mat(image_crop.rows, image_crop.cols, CV_8UC1, cv::Scalar(1));
                image_crop.copyTo(roi, mask);
                list.push_back(Image(background));
            }
        }
    }
    return list;
}

#ifdef AMBER_USE_CAFFE_BLOB

/**
 * 转换为 caffe::Blob<float>*  格式
 */
caffe::Blob<float>* Image::asBlob()
{
    int height = this->image.rows;
    int width = this->image.cols;
    int channels = this->image.channels();

    caffe::Blob<float>* blob = new caffe::Blob<float>(1, channels, height, width);

    uchar* imageBytes = this->image.data;
    float* imageFloatData = blob->mutable_cpu_data();

    // 转换注意三点：
    // 1.将整数转为浮点
    // 2.除255，标准化（所有像素在0-1之间）
    // 3.将HWC主序转换为NCHW
    for (int h = 0; h < height; ++h) {
        const uchar* ptr = imageBytes + h * width * channels;
        int pixelIndex = 0;
        for (int w = 0; w < width; ++w) {
            for (int c = 0; c < channels; ++c) {
                int datumIndex = (c * height + h) * width + w;
                imageFloatData[datumIndex] = static_cast<float>(ptr[pixelIndex]) / 255.0f;
                ++pixelIndex;
            }
        }
    }
    return blob;
}
#endif // AMBER_USE_CAFFE_BLOB
}
